/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "Hardware.h"
#include <logs/Log.h>
#include <Gluck.h>

namespace liba {
namespace lib3d {
namespace hard {

int Hardware::get_mode_count()
{
	return attribute_table.size();
}

void Hardware::set_attribute_names(const Strings & names, const Strings & values)
{
	if( names.size() != values.size() )
		THROW(Gluck(__FUNCTION__, "names.size() != values.size()"));
	attribute_names = names;
	preferred_attribute_values = values;
	for( int i = 0; i != names.size(); ++i )
		logs::log() << " | " << names[i];
	logs::log() << "\n";
	for( int i = 0; i != values.size(); ++i )
		logs::log() << " | " << values[i];
	logs::log() << "\n";
	logs::log() << "-------------------------------------------------------------\n";
}

static int calc_simple_deviation( const Strings & current, const Strings & preferred )
{
	int deviation = 0;
	for( int p = 0; p != current.size(); ++p )
	{
		if( current[p] == preferred[p] )
			deviation += 0;
		else
			deviation += 1;
		deviation *= 3;
	}
	return deviation;
}

void Hardware::add_attribute_table_row(const Strings & values)
{
	if( attribute_names.size() != values.size() )
		THROW(Gluck(__FUNCTION__, "attribute_names.size() != values.size()"));
	for( int i = 0; i != values.size(); ++i )
		logs::log() << " | " << values[i];
	logs::log() << "\n";

	attribute_table.push_back( values );
	int old_dev = calc_simple_deviation( attribute_table[current_selection], preferred_attribute_values );
	int new_dev = calc_simple_deviation( attribute_table.back(), preferred_attribute_values );
	if( new_dev < old_dev )
		current_selection = attribute_table.size() - 1;
}

bool Hardware::set_attribute_preference( const std::string & name, const std::string & value )
{
	size_t pos = std::find( attribute_names.begin(), attribute_names.end(), name ) - attribute_names.begin();
	if( pos >= attribute_names.size() )
		return false;
	preferred_attribute_values[ pos ] = value;
	
	if( attribute_table.empty() )
		return true;

	current_selection = 0;
	int dev = calc_simple_deviation( attribute_table[current_selection], preferred_attribute_values );

	StringTable::iterator stit = attribute_table.begin();
	for( int curr = 0; stit != attribute_table.end(); ++stit, ++curr )
	{
		int new_dev = calc_simple_deviation( *stit, preferred_attribute_values );
		if( new_dev < dev )
		{
			dev = new_dev;
			current_selection = curr;
		}
	}
	return true;
}

bool Hardware::set_attribute_filter( const std::string & name, const Strings & values )
{
	size_t pos = std::find( attribute_names.begin(), attribute_names.end(), name ) - attribute_names.begin();
	if( pos >= attribute_names.size() )
		return false;
	StringTable new_table;
	StringTable::iterator stit = attribute_table.begin();
	for( ;stit != attribute_table.end(); ++stit )
		if( std::find( values.begin(), values.end(), (*stit)[pos] ) != values.end() )
			new_table.push_back( *stit );
	attribute_table.swap( new_table );
	if( attribute_table.empty() )
		return false; // TODO: ???

	current_selection = 0;
	int dev = calc_simple_deviation( attribute_table[current_selection], preferred_attribute_values );

	stit = attribute_table.begin();
	for( int curr = 0; stit != attribute_table.end(); ++stit, ++curr )
	{
		int new_dev = calc_simple_deviation( *stit, preferred_attribute_values );
		if( new_dev < dev )
		{
			dev = new_dev;
			current_selection = curr;
		}
	}
	return true;
}

const Strings & Hardware::GetAttribute_names()
{
	return attribute_names;
}

std::string Hardware::GetAttribute(const std::string & name)
{
	size_t pos = std::find( attribute_names.begin(), attribute_names.end(), name ) - attribute_names.begin();
	if( pos >= attribute_names.size() )
		return std::string();
	if( attribute_table.empty() )
		return std::string();
	return attribute_table[current_selection][pos];
}

bool Hardware::set_attribute(const std::string & name, const std::string & value)
{
	size_t pos = std::find( attribute_names.begin(), attribute_names.end(), name ) - attribute_names.begin();
	if( pos >= attribute_names.size() )
		return false;
	StringTable::const_iterator stit = attribute_table.begin();
	int new_selection = attribute_table.size();
	int new_deviation = 10000;
	for( int curr = 0; stit != attribute_table.end(); ++stit, ++curr )
	{
		int p = 0;
		for( ; p != pos; ++p )
			if( (*stit)[p] != attribute_table[current_selection][p] )
				break;
		if( p != pos )
			continue;
		if( (*stit)[pos] != value )
			continue;
		int deviation = 0;
		for( p = pos; p != attribute_names.size(); ++p )
		{
			if( (*stit)[p] == attribute_table[current_selection][p] )
				deviation += 0;
			else
			if( (*stit)[p] == preferred_attribute_values[p] )
				deviation += 1;
			else
				deviation += 2;
			deviation *= 3;
		}
		if( deviation < new_deviation )
		{
			new_selection = curr;
			new_deviation = deviation;
		}
	}
	if( new_selection == attribute_table.size() )
		return false;
	current_selection = new_selection;
	return true;
}

Strings Hardware::GetAttribute_choices(const std::string & name)
{
	size_t pos = std::find( attribute_names.begin(), attribute_names.end(), name ) - attribute_names.begin();
	if( pos >= attribute_names.size() )
		return Strings();
	Strings result;
	StringTable::const_iterator stit = attribute_table.begin();
	for( ;stit != attribute_table.end(); ++stit )
	{
		int p = 0;
		for( ; p != pos; ++p )
			if( (*stit)[p] != attribute_table[current_selection][p] )
				break;
		if( p == pos )
			result.push_back( (*stit)[pos] );
	}
	{
		//we should not spoil the order
		//std::sort( result.begin(), result.end() );
		//result.erase( std::unique( result.begin(), result.end() ), result.end() );
start_again:
		for( size_t i = 0; i < result.size() - 1; ++i )
		for( size_t j = i + 1; j < result.size(); ++j )
			if( result[i] == result[j] )
			{
				result.erase( result.begin() + j );
				goto start_again;
			}		
	}
	return result;
}

bool Hardware::set_mode(WindowImpl * window)
{
#if !defined(IPHONE_VERSION) && !defined(ANDROID_NDK) && !defined(SAMSUNG_VERSION) && !defined(MARMALADE)
	if( attribute_table.empty() )
	{
	    logs::log() << "Hardware Selector: empty attributes table, bailing out";
		return false;
	}
#endif
	return set_mode_internal( window, attribute_table[current_selection] );
}

} // namespace hard
} // namespace lib3d
} // namespace liba

